#include "tcp_client.h"
#include "tcp_server.h"
#include "utils.h"
#include <iostream>
#include <thread>
#include <cstring>
#include <cmath>
#include <vector>
#include <unistd.h>

namespace OHOS {
namespace DistributedHardware {

const int32_t TEST_TIMES = 10;
const uint16_t port = 9000;
std::string svr_ip = "127.0.0.1";
std::vector<int64_t> timeStampDiffs;
std::vector<int64_t> transCosts;
TcpClient tc;

void PrinResult()
{
    int64_t sumDiff = 0;
    int64_t sumTransCost = 0;

    for (int32_t i = 0; i < TEST_TIMES; i++) {
        sumDiff += timeStampDiffs[i];
        sumTransCost += transCosts[i];
    }

    std::cout << "average TimeStampDiff(server - client)(ms):" << sumDiff / TEST_TIMES <<std::endl;
    std::cout << "average transport cost(ms):" << sumTransCost / TEST_TIMES <<std::endl;
}

void DoCheck()
{
    timed.t1 = GetCurrentTime();
    std::cout << "timed.t1:" << timed.t1 << std::endl;
    char sendBuff[BUFF_LENTH] = {0};
    sprintf(sendBuff, "Hi I'm Client");
    int ret = tc.SendData(sendBuff, sizeof(sendBuff));
    if (ret <= 0) {
        return;
    }
    char recvBuff[BUFF_LENTH] = {0};
    ret = tc.RecvData(recvBuff, sizeof(recvBuff));
    if (ret <= 0) {
        return;
    }
    TimeData* res = (TimeData*)recvBuff;
    timed.t4 = GetCurrentTime();
    std::cout << "timed.t2:" << res->t2 << std::endl;
    std::cout << "timed.t3:" << res->t3 << std::endl;
    std::cout << "server hostname:" << res->hostname << std::endl;
    std::cout << "timed.t4:" << timed.t4 << std::endl;

    int64_t timeDiff = llabs(((int64_t)(res->t2 - timed.t1) - (int64_t)(timed.t4 - res->t3)) / 2);
    int64_t transCost = ((int64_t)(res->t2 - timed.t1) + (int64_t)(timed.t4 - res->t3)) / 2;
    std::cout << "TimeStampDiff: server - client =" << timeDiff << std::endl;
    std::cout << "Trans msg between client and server cost:" << transCost << std::endl;
    timeStampDiffs.push_back(timeDiff);
    transCosts.push_back(transCost);
}

void TcpClientInit ()
{
    std::cout << "TcpClientInit start" << std::endl;
    if (tc.TcpConnectSocket(svr_ip.c_str(), port) == INVALID_SOCKET) {
        std::cout << "client link false" << std::endl;
        return;
    }
    char name[NAME_LENTH] = {0};
    gethostname(name, sizeof(name));
    std::cout << "client name:" << name << std::endl;

    for (int32_t i = 1; i <= TEST_TIMES; i++) {
        std::cout << "-----------------Test idx:" << i << "-----------------" <<std::endl;
        DoCheck();
    }
    PrinResult();
    tc.CloseSocket();
}

void TcpResponse(TcpClient* tcpc)
{
    while (true) {
        char recvBuff[BUFF_LENTH] = {0};
        int ret = tcpc->RecvData(recvBuff, sizeof(recvBuff));
        if (ret <= 0) {
            return;
        }
        std::cout << recvBuff << std::endl;

        timed.t2 = GetCurrentTime();
        timed.t3 = GetCurrentTime();
        std::cout << "timed.t2:" << timed.t2 << std::endl;
        std::cout << "timed.t3:" << timed.t3 << std::endl;

        char name[NAME_LENTH] = {0};
        gethostname(name, sizeof(name));
        strcpy(timed.hostname, name);
        std::cout << "timed.hostname:" << timed.hostname << "\n" << std::endl;
        ret = tcpc->SendData((char*)&timed, sizeof(timed));
        if (ret <= 0) {
            return;
        }
    }
}

void TcpServerInit()
{
    std::cout << "TcpServerInit start" << std::endl;
    TcpServer ts;
    if (!ts.TcpServerSocket(port)) {
        std::cout << "TcpServerSocket fail" << std::endl;
        return;
    }
    while (true) {
        TcpClient* tcpc = new TcpClient();
        if (!ts.Accept(tcpc)) {
            std::cout << "accept fail" << std::endl;
            return;
        }
        std::thread RecvAndSend(TcpResponse, tcpc);
        RecvAndSend.detach();
    }
}
}
}
int main(int argc, char* argv[])
{
    OHOS::DistributedHardware::timeStampDiffs.clear();
    OHOS::DistributedHardware::transCosts.clear();
    /**
     * --s: creat as server mode
     * --c: creat as client mode, there must be the IP if the server as argument
     */
    const char* optstring = "sc:";
    int opt = -1;
    while ((opt = getopt(argc, argv, optstring)) != -1) {
        switch (opt) {
            case 's':
                OHOS::DistributedHardware::TcpServerInit();
                break;
            case 'c':
                OHOS::DistributedHardware::svr_ip = optarg;
                OHOS::DistributedHardware::TcpClientInit();
                break;
            case '?':
                std::cout << "error optopt:" << optopt <<std::endl;
                std::cout << "error opterr:" << opterr <<std::endl;
                break;
        }
    }
    return 0;
}

